1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.codehaus.groovy.classgen.asm;
20
21 import org.codehaus.groovy.GroovyBugError;
22 import org.codehaus.groovy.ast.ClassHelper;
23 import org.codehaus.groovy.ast.ClassNode;
24 import org.codehaus.groovy.ast.MethodNode;
25 import org.codehaus.groovy.ast.Parameter;
26 import org.codehaus.groovy.ast.Variable;
27 import org.codehaus.groovy.ast.VariableScope;
28 import org.objectweb.asm.Label;
29 import org.objectweb.asm.MethodVisitor;
30 import org.objectweb.asm.Opcodes;
31
32 import java.util.*;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class CompileStack implements Opcodes {
67
68
69
70
71
72
73 private boolean clear=true;
74
75 private VariableScope scope;
76
77 private Label continueLabel;
78
79 private Label breakLabel;
80
81 private Map stackVariables = new HashMap();
82
83 private int currentVariableIndex = 1;
84
85 private int nextVariableIndex = 1;
86
87 private final LinkedList temporaryVariables = new LinkedList();
88
89 private final LinkedList usedVariables = new LinkedList();
90
91 private Map superBlockNamedLabels = new HashMap();
92
93 private Map currentBlockNamedLabels = new HashMap();
94
95
96
97 private LinkedList<BlockRecorder> finallyBlocks = new LinkedList<BlockRecorder>();
98 private LinkedList<BlockRecorder> visitedBlocks = new LinkedList<BlockRecorder>();
99
100 private Label thisStartLabel, thisEndLabel;
101
102
103
104
105 private final LinkedList stateStack = new LinkedList();
106
107
108 private LinkedList<Boolean> implicitThisStack = new LinkedList();
109
110 private LinkedList<Boolean> lhsStack = new LinkedList();
111 {
112 implicitThisStack.add(false);
113 lhsStack.add(false);
114 }
115
116
117
118 private int localVariableOffset;
119
120
121 private final Map namedLoopBreakLabel = new HashMap();
122
123
124 private final Map namedLoopContinueLabel = new HashMap();
125 private String className;
126 private LinkedList<ExceptionTableEntry> typedExceptions = new LinkedList<ExceptionTableEntry>();
127 private LinkedList<ExceptionTableEntry> untypedExceptions = new LinkedList<ExceptionTableEntry>();
128
129 private boolean lhs;
130
131 private boolean implicitThis;
132 private WriterController controller;
133 private boolean inSpecialConstructallCall;
134
135 protected static class LabelRange {
136 public Label start;
137 public Label end;
138 }
139
140 public static class BlockRecorder {
141 private boolean isEmpty = true;
142 public Runnable excludedStatement;
143 public LinkedList<LabelRange> ranges;
144 public BlockRecorder() {
145 ranges = new LinkedList<LabelRange>();
146 }
147 public BlockRecorder(Runnable excludedStatement) {
148 this();
149 this.excludedStatement = excludedStatement;
150 }
151 public void startRange(Label start) {
152 LabelRange range = new LabelRange();
153 range.start = start;
154 ranges.add(range);
155 isEmpty = false;
156 }
157 public void closeRange(Label end) {
158 ranges.getLast().end = end;
159 }
160 }
161
162 private class ExceptionTableEntry {
163 Label start,end,goal;
164 String sig;
165 }
166
167 private class StateStackElement {
168 final VariableScope scope;
169 final Label continueLabel;
170 final Label breakLabel;
171 final Map stackVariables;
172 final Map currentBlockNamedLabels;
173 final LinkedList<BlockRecorder> finallyBlocks;
174 final boolean inSpecialConstructallCall;
175
176 StateStackElement() {
177 scope = CompileStack.this.scope;
178 continueLabel = CompileStack.this.continueLabel;
179 breakLabel = CompileStack.this.breakLabel;
180 stackVariables = CompileStack.this.stackVariables;
181 currentBlockNamedLabels = CompileStack.this.currentBlockNamedLabels;
182 finallyBlocks = CompileStack.this.finallyBlocks;
183 inSpecialConstructallCall = CompileStack.this.inSpecialConstructallCall;
184 }
185 }
186
187 public CompileStack(WriterController wc) {
188 this.controller = wc;
189 }
190
191 public void pushState() {
192 stateStack.add(new StateStackElement());
193 stackVariables = new HashMap(stackVariables);
194 finallyBlocks = new LinkedList(finallyBlocks);
195 }
196
197 private void popState() {
198 if (stateStack.size()==0) {
199 throw new GroovyBugError("Tried to do a pop on the compile stack without push.");
200 }
201 StateStackElement element = (StateStackElement) stateStack.removeLast();
202 scope = element.scope;
203 continueLabel = element.continueLabel;
204 breakLabel = element.breakLabel;
205 stackVariables = element.stackVariables;
206 finallyBlocks = element.finallyBlocks;
207 inSpecialConstructallCall = element.inSpecialConstructallCall;
208 }
209
210 public Label getContinueLabel() {
211 return continueLabel;
212 }
213
214 public Label getBreakLabel() {
215 return breakLabel;
216 }
217
218 public void removeVar(int tempIndex) {
219 final BytecodeVariable head = (BytecodeVariable) temporaryVariables.removeFirst();
220 if (head.getIndex() != tempIndex) {
221 temporaryVariables.addFirst(head);
222 MethodNode methodNode = controller.getMethodNode();
223 if (methodNode==null) {
224 methodNode = controller.getConstructorNode();
225 }
226 throw new GroovyBugError(
227 "In method "+ (methodNode!=null?methodNode.getText():"<unknown>") + ", " +
228 "CompileStack#removeVar: tried to remove a temporary " +
229 "variable with index "+ tempIndex + " in wrong order. " +
230 "Current temporary variables=" + temporaryVariables);
231 }
232 }
233
234 private void setEndLabels(){
235 Label endLabel = new Label();
236 controller.getMethodVisitor().visitLabel(endLabel);
237 for (Iterator iter = stackVariables.values().iterator(); iter.hasNext();) {
238 BytecodeVariable var = (BytecodeVariable) iter.next();
239 var.setEndLabel(endLabel);
240 }
241 thisEndLabel = endLabel;
242 }
243
244 public void pop() {
245 setEndLabels();
246 popState();
247 }
248
249 public VariableScope getScope() {
250 return scope;
251 }
252
253
254
255
256
257
258
259
260 public int defineTemporaryVariable(org.codehaus.groovy.ast.Variable var, boolean store) {
261 return defineTemporaryVariable(var.getName(), var.getType(),store);
262 }
263
264 public BytecodeVariable getVariable(String variableName ) {
265 return getVariable(variableName, true);
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283 public BytecodeVariable getVariable(String variableName, boolean mustExist) {
284 if (variableName.equals("this")) return BytecodeVariable.THIS_VARIABLE;
285 if (variableName.equals("super")) return BytecodeVariable.SUPER_VARIABLE;
286 BytecodeVariable v = (BytecodeVariable) stackVariables.get(variableName);
287 if (v == null && mustExist)
288 throw new GroovyBugError("tried to get a variable with the name " + variableName + " as stack variable, but a variable with this name was not created");
289 return v;
290 }
291
292
293
294
295
296
297
298
299 public int defineTemporaryVariable(String name,boolean store) {
300 return defineTemporaryVariable(name, ClassHelper.DYNAMIC_TYPE,store);
301 }
302
303
304
305
306
307
308
309
310
311 public int defineTemporaryVariable(String name, ClassNode node, boolean store) {
312 BytecodeVariable answer = defineVar(name, node, false, false);
313 temporaryVariables.addFirst(answer);
314 usedVariables.removeLast();
315
316 if (store) controller.getOperandStack().storeVar(answer);
317
318 return answer.getIndex();
319 }
320
321 private void resetVariableIndex(boolean isStatic) {
322 temporaryVariables.clear();
323 if (!isStatic) {
324 currentVariableIndex=1;
325 nextVariableIndex=1;
326 } else {
327 currentVariableIndex=0;
328 nextVariableIndex=0;
329 }
330 }
331
332
333
334
335
336
337 public void clear() {
338 if (stateStack.size()>1) {
339 int size = stateStack.size()-1;
340 throw new GroovyBugError("the compile stack contains "+size+" more push instruction"+(size==1?"":"s")+" than pops.");
341 }
342 if (lhsStack.size()>1) {
343 int size = lhsStack.size()-1;
344 throw new GroovyBugError("lhs stack is supposed to be empty, but has " +
345 size + " elements left.");
346 }
347 if (implicitThisStack.size()>1) {
348 int size = implicitThisStack.size()-1;
349 throw new GroovyBugError("implicit 'this' stack is supposed to be empty, but has " +
350 size + " elements left.");
351 }
352 clear = true;
353 MethodVisitor mv = controller.getMethodVisitor();
354
355 if (true) {
356 if (thisEndLabel==null) setEndLabels();
357
358 if (!scope.isInStaticContext()) {
359
360 mv.visitLocalVariable("this", className, null, thisStartLabel, thisEndLabel, 0);
361 }
362
363 for (Iterator iterator = usedVariables.iterator(); iterator.hasNext();) {
364 BytecodeVariable v = (BytecodeVariable) iterator.next();
365 ClassNode t = v.getType();
366 if (v.isHolder()) t = ClassHelper.REFERENCE_TYPE;
367 String type = BytecodeHelper.getTypeDescription(t);
368 Label start = v.getStartLabel();
369 Label end = v.getEndLabel();
370 mv.visitLocalVariable(v.getName(), type, null, start, end, v.getIndex());
371 }
372 }
373
374
375 for (ExceptionTableEntry ep : typedExceptions) {
376 mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
377 }
378
379 for (ExceptionTableEntry ep : untypedExceptions) {
380 mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
381 }
382
383
384 pop();
385 typedExceptions.clear();
386 untypedExceptions.clear();
387 stackVariables.clear();
388 usedVariables.clear();
389 scope = null;
390 finallyBlocks.clear();
391 mv=null;
392 resetVariableIndex(false);
393 superBlockNamedLabels.clear();
394 currentBlockNamedLabels.clear();
395 namedLoopBreakLabel.clear();
396 namedLoopContinueLabel.clear();
397 continueLabel=null;
398 breakLabel=null;
399 thisStartLabel=null;
400 thisEndLabel=null;
401 mv = null;
402 }
403
404 public void addExceptionBlock (Label start, Label end, Label goal,
405 String sig)
406 {
407
408
409 ExceptionTableEntry ep = new ExceptionTableEntry();
410 ep.start = start;
411 ep.end = end;
412 ep.sig = sig;
413 ep.goal = goal;
414 if (sig==null) {
415 untypedExceptions.add(ep);
416 } else {
417 typedExceptions.add(ep);
418 }
419 }
420
421
422
423
424
425
426
427
428 public void init(VariableScope el, Parameter[] parameters) {
429 if (!clear) throw new GroovyBugError("CompileStack#init called without calling clear before");
430 clear=false;
431 pushVariableScope(el);
432 defineMethodVariables(parameters,el.isInStaticContext());
433 this.className = BytecodeHelper.getTypeDescription(controller.getClassNode());
434 }
435
436
437
438
439
440
441 public void pushVariableScope(VariableScope el) {
442 pushState();
443 scope = el;
444 superBlockNamedLabels = new HashMap(superBlockNamedLabels);
445 superBlockNamedLabels.putAll(currentBlockNamedLabels);
446 currentBlockNamedLabels = new HashMap();
447 }
448
449
450
451
452
453
454
455 public void pushLoop(VariableScope el, String labelName) {
456 pushVariableScope(el);
457 continueLabel = new Label();
458 breakLabel = new Label();
459 if (labelName != null) {
460 initLoopLabels(labelName);
461 }
462 }
463
464
465
466
467
468
469
470 public void pushLoop(VariableScope el, List<String> labelNames) {
471 pushVariableScope(el);
472 continueLabel = new Label();
473 breakLabel = new Label();
474 if (labelNames != null) {
475 for (String labelName : labelNames) {
476 initLoopLabels(labelName);
477 }
478 }
479 }
480
481 private void initLoopLabels(String labelName) {
482 namedLoopBreakLabel.put(labelName,breakLabel);
483 namedLoopContinueLabel.put(labelName,continueLabel);
484 }
485
486
487
488
489
490
491 public void pushLoop(String labelName) {
492 pushState();
493 continueLabel = new Label();
494 breakLabel = new Label();
495 initLoopLabels(labelName);
496 }
497
498
499
500
501
502
503 public void pushLoop(List<String> labelNames) {
504 pushState();
505 continueLabel = new Label();
506 breakLabel = new Label();
507 if (labelNames != null) {
508 for (String labelName : labelNames) {
509 initLoopLabels(labelName);
510 }
511 }
512 }
513
514
515
516
517
518
519
520 public Label getNamedBreakLabel(String name) {
521 Label label = getBreakLabel();
522 Label endLabel = null;
523 if (name!=null) endLabel = (Label) namedLoopBreakLabel.get(name);
524 if (endLabel!=null) label = endLabel;
525 return label;
526 }
527
528
529
530
531
532
533
534 public Label getNamedContinueLabel(String name) {
535 Label label = getLabel(name);
536 Label endLabel = null;
537 if (name!=null) endLabel = (Label) namedLoopContinueLabel.get(name);
538 if (endLabel!=null) label = endLabel;
539 return label;
540 }
541
542
543
544
545
546 public Label pushSwitch(){
547 pushState();
548 breakLabel = new Label();
549 return breakLabel;
550 }
551
552
553
554
555
556 public void pushBooleanExpression(){
557 pushState();
558 }
559
560 private BytecodeVariable defineVar(String name, ClassNode type, boolean holder, boolean useReferenceDirectly) {
561 int prevCurrent = currentVariableIndex;
562 makeNextVariableID(type,useReferenceDirectly);
563 int index = currentVariableIndex;
564 if (holder && !useReferenceDirectly) index = localVariableOffset++;
565 BytecodeVariable answer = new BytecodeVariable(index, type, name, prevCurrent);
566 usedVariables.add(answer);
567 answer.setHolder(holder);
568 return answer;
569 }
570
571 private void makeLocalVariablesOffset(Parameter[] paras,boolean isInStaticContext) {
572 resetVariableIndex(isInStaticContext);
573
574 for (int i = 0; i < paras.length; i++) {
575 makeNextVariableID(paras[i].getType(),false);
576 }
577 localVariableOffset = nextVariableIndex;
578
579 resetVariableIndex(isInStaticContext);
580 }
581
582 private void defineMethodVariables(Parameter[] paras, boolean isInStaticContext) {
583 Label startLabel = new Label();
584 thisStartLabel = startLabel;
585 controller.getMethodVisitor().visitLabel(startLabel);
586
587 makeLocalVariablesOffset(paras,isInStaticContext);
588
589 for (int i = 0; i < paras.length; i++) {
590 String name = paras[i].getName();
591 BytecodeVariable answer;
592 ClassNode type = paras[i].getType();
593 if (paras[i].isClosureSharedVariable()) {
594 boolean useExistingReference = paras[i].getNodeMetaData(ClosureWriter.UseExistingReference.class) != null;
595 answer = defineVar(name, paras[i].getOriginType(), true, useExistingReference);
596 answer.setStartLabel(startLabel);
597 if (!useExistingReference) {
598 controller.getOperandStack().load(type,currentVariableIndex);
599 controller.getOperandStack().box();
600
601
602
603
604
605
606 Label newStart = new Label();
607 controller.getMethodVisitor().visitLabel(newStart);
608 BytecodeVariable var = new BytecodeVariable(currentVariableIndex, paras[i].getOriginType(), name, currentVariableIndex);
609 var.setStartLabel(startLabel);
610 var.setEndLabel(newStart);
611 usedVariables.add(var);
612 answer.setStartLabel(newStart);
613
614 createReference(answer);
615 }
616 } else {
617 answer = defineVar(name, type, false, false);
618 answer.setStartLabel(startLabel);
619 }
620 stackVariables.put(name, answer);
621 }
622
623 nextVariableIndex = localVariableOffset;
624 }
625
626 private void createReference(BytecodeVariable reference) {
627 MethodVisitor mv = controller.getMethodVisitor();
628 mv.visitTypeInsn(NEW, "groovy/lang/Reference");
629 mv.visitInsn(DUP_X1);
630 mv.visitInsn(SWAP);
631 mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V", false);
632 mv.visitVarInsn(ASTORE, reference.getIndex());
633 }
634
635 private void pushInitValue(ClassNode type, MethodVisitor mv) {
636 if (ClassHelper.isPrimitiveType(type)) {
637 if (type==ClassHelper.long_TYPE) {
638 mv.visitInsn(LCONST_0);
639 } else if (type==ClassHelper.double_TYPE) {
640 mv.visitInsn(DCONST_0);
641 } else if (type==ClassHelper.float_TYPE) {
642 mv.visitInsn(FCONST_0);
643 } else {
644 mv.visitLdcInsn(0);
645 }
646 } else {
647 mv.visitInsn(ACONST_NULL);
648 }
649 }
650
651
652
653
654
655
656
657
658 public BytecodeVariable defineVariable(Variable v, boolean initFromStack) {
659 return defineVariable(v, v.getOriginType(), initFromStack);
660 }
661 public BytecodeVariable defineVariable(Variable v, ClassNode variableType, boolean initFromStack) {
662
663
664
665 String name = v.getName();
666 BytecodeVariable answer = defineVar(name, variableType, v.isClosureSharedVariable(), v.isClosureSharedVariable());
667 stackVariables.put(name, answer);
668
669 MethodVisitor mv = controller.getMethodVisitor();
670 Label startLabel = new Label();
671 answer.setStartLabel(startLabel);
672 ClassNode type = answer.getType().redirect();
673 OperandStack operandStack = controller.getOperandStack();
674
675 if (!initFromStack) pushInitValue(type, mv);
676 operandStack.push(answer.getType());
677 if (answer.isHolder()) {
678 operandStack.box();
679 operandStack.remove(1);
680 createReference(answer);
681 } else {
682 operandStack.storeVar(answer);
683 }
684
685 mv.visitLabel(startLabel);
686 return answer;
687 }
688
689
690
691
692
693 public boolean containsVariable(String name) {
694 return stackVariables.containsKey(name);
695 }
696
697
698
699
700
701 private void makeNextVariableID(ClassNode type, boolean useReferenceDirectly) {
702 currentVariableIndex = nextVariableIndex;
703 if ((type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE) && !useReferenceDirectly) {
704 nextVariableIndex++;
705 }
706 nextVariableIndex++;
707 }
708
709
710
711
712 public Label getLabel(String name) {
713 if (name==null) return null;
714 Label l = (Label) superBlockNamedLabels.get(name);
715 if (l==null) l = createLocalLabel(name);
716 return l;
717 }
718
719
720
721
722 public Label createLocalLabel(String name) {
723 Label l = (Label) currentBlockNamedLabels.get(name);
724 if (l==null) {
725 l = new Label();
726 currentBlockNamedLabels.put(name,l);
727 }
728 return l;
729 }
730
731 public void applyFinallyBlocks(Label label, boolean isBreakLabel) {
732
733
734
735
736 StateStackElement result = null;
737 for (ListIterator iter = stateStack.listIterator(stateStack.size()); iter.hasPrevious();) {
738 StateStackElement element = (StateStackElement) iter.previous();
739 if (!element.currentBlockNamedLabels.values().contains(label)) {
740 if (isBreakLabel && element.breakLabel != label) {
741 result = element;
742 break;
743 }
744 if (!isBreakLabel && element.continueLabel != label) {
745 result = element;
746 break;
747 }
748 }
749 }
750
751 List<BlockRecorder> blocksToRemove;
752 if (result==null) {
753
754 blocksToRemove = (List<BlockRecorder>) Collections.EMPTY_LIST;
755 } else {
756 blocksToRemove = result.finallyBlocks;
757 }
758
759 List<BlockRecorder> blocks = new LinkedList<BlockRecorder>(finallyBlocks);
760 blocks.removeAll(blocksToRemove);
761 applyBlockRecorder(blocks);
762 }
763
764 private void applyBlockRecorder(List<BlockRecorder> blocks) {
765 if (blocks.size()==0 || blocks.size()==visitedBlocks.size()) return;
766
767 MethodVisitor mv = controller.getMethodVisitor();
768
769 Label end = new Label();
770 mv.visitInsn(NOP);
771 mv.visitLabel(end);
772 Label newStart = new Label();
773
774 for (BlockRecorder fb : blocks) {
775 if (visitedBlocks.contains(fb)) continue;
776
777 fb.closeRange(end);
778
779
780
781 fb.excludedStatement.run();
782
783 fb.startRange(newStart);
784 }
785
786 mv.visitInsn(NOP);
787 mv.visitLabel(newStart);
788 }
789
790 public void applyBlockRecorder() {
791 applyBlockRecorder(finallyBlocks);
792 }
793
794 public boolean hasBlockRecorder() {
795 return !finallyBlocks.isEmpty();
796 }
797
798 public void pushBlockRecorder(BlockRecorder recorder) {
799 pushState();
800 finallyBlocks.addFirst(recorder);
801 }
802
803 public void pushBlockRecorderVisit(BlockRecorder finallyBlock) {
804 visitedBlocks.add(finallyBlock);
805 }
806
807 public void popBlockRecorderVisit(BlockRecorder finallyBlock) {
808 visitedBlocks.remove(finallyBlock);
809 }
810
811 public void writeExceptionTable(BlockRecorder block, Label goal, String sig) {
812 if (block.isEmpty) return;
813 MethodVisitor mv = controller.getMethodVisitor();
814 for (LabelRange range : block.ranges) {
815 mv.visitTryCatchBlock(range.start, range.end, goal, sig);
816 }
817 }
818
819
820
821
822
823 public boolean isLHS() {
824 return lhs;
825 }
826
827 public void pushLHS(boolean lhs) {
828 lhsStack.add(lhs);
829 this.lhs = lhs;
830 }
831
832 public void popLHS() {
833 lhsStack.removeLast();
834 this.lhs = lhsStack.getLast();
835 }
836
837 public void pushImplicitThis(boolean implicitThis) {
838 implicitThisStack.add(implicitThis);
839 this.implicitThis = implicitThis;
840 }
841
842 public boolean isImplicitThis() {
843 return implicitThis;
844 }
845
846 public void popImplicitThis() {
847 implicitThisStack.removeLast();
848 this.implicitThis = implicitThisStack.getLast();
849 }
850
851 public boolean isInSpecialConstructorCall() {
852 return inSpecialConstructallCall;
853 }
854
855 public void pushInSpecialConstructorCall() {
856 pushState();
857 inSpecialConstructallCall = true;
858 }
859 }